<script setup>
import Tabs from "../plugins/tabs.vue";
import TabItem from "../plugins/tab-item.vue";
import Items from "../plugins/items.vue"
import Item from "../plugins/item.vue"
import Pageul from "../plugins/pageul.vue"
import Pageli from "../plugins/pageli.vue"
import Panel from "./panel.vue";
import { Doughnut } from "vue-chartjs";
import { vOnClickOutside } from '@vueuse/components'
import JsonEditor from "../plugins/json-editor.vue"
import { ref, computed, onBeforeMount, nextTick, watch } from "vue";
import {
  Chart as ChartJS,
  Title,
  Tooltip,
  Legend,
  ArcElement,
  CategoryScale,
} from "chart.js";
import RecordPanel from "./items/RecordPanel.vue"
import MyDialog from "../plugins/dialog.vue"

import TableIcon from "../assets/table.svg?component"
import ChartIcon from "../assets/chart.svg?component"
import FilterIcon from "../assets/filter.svg?component"
import { filter } from "lodash";

ChartJS.register(Title, Tooltip, Legend, ArcElement, CategoryScale);
ChartJS.register({
  id: "center-text-plugin",
  beforeDraw: function (chart) {
    if (chart.config.options.elements?.center) {
      // Get ctx from string
      var ctx = chart.ctx;

      // Get options from the center object in options
      var centerConfigs = chart.config.options.elements.center;

      var innerRadius = chart._metasets[chart._metasets.length - 1].data[0].innerRadius;

      for (let index = 0; index < centerConfigs.length; index++) {
        const centerConfig = centerConfigs[index];
        var fontStyle = centerConfig.fontStyle || 'Arial';
        var txt = centerConfig.text;
        var color = centerConfig.color || '#000';
        var maxFontSize = centerConfig.maxFontSize || 75;
        var sidePadding = centerConfig.sidePadding || 40;
        var sidePaddingCalculated = (sidePadding / 100) * (innerRadius * 2)
        var fontSize = centerConfig.size || 1;
        // Start with a base font of 30px
        ctx.font = "30px " + fontStyle;

        // Get the width of the string and also the width of the element minus 10 to give it 5px side padding
        var stringWidth = ctx.measureText(txt).width;
        var elementWidth = (innerRadius * 2) - sidePaddingCalculated;

        // Find out how much the font can grow in width.
        var widthRatio = elementWidth / stringWidth;
        var newFontSize = Math.floor(30 * widthRatio * fontSize);
        var elementHeight = (innerRadius * 2);

        // Pick a new font size so it will not be larger than the height of label.
        var fontSizeToUse = Math.min(newFontSize, elementHeight, maxFontSize);
        var minFontSize = centerConfig.minFontSize;
        var lineHeight = centerConfig.lineHeight || 50;
        var wrapText = false;

        if (minFontSize === undefined) {
          minFontSize = 20;
        }

        if (minFontSize && fontSizeToUse < minFontSize) {
          fontSizeToUse = minFontSize;
          wrapText = true;
        }

        // Set font settings to draw it correctly.
        ctx.textAlign = 'center';
        ctx.textBaseline = 'middle';
        var centerX = ((chart.chartArea.left + chart.chartArea.right) / 2);
        var centerY = ((chart.chartArea.top + chart.chartArea.bottom) / 2);
        ctx.font = fontSizeToUse + "px " + fontStyle;
        ctx.fillStyle = color;

        // Move the center up depending on line height and number of lines
        centerY += -(centerConfigs.length - 1) / 2 * lineHeight + index * lineHeight;

        //Draw text in center
        ctx.fillText(txt, centerX, centerY);
      }


    }
  }
});

const labelDeses = computed(() => {
    return Levels;
})
const shownEc = ref(Object.fromEntries(Object.entries(Levels).map(arr => {
  arr[1] = true;
  return arr
})))
const result = computed(() => {
  return analysis_result;
});
const briefTotal = computed(() => {
  return Object.values(result.value.Brief).reduce((prev, cur) => prev + cur, 0)
})
const briefData = computed(() => {
  var labels = []
  var data = []
  var valuesArr = Object.entries(result.value.Brief);
  labels = valuesArr.map(([key, data]) => Levels[key])
  if (!staticFiltered.value) {
    data = valuesArr.map(([key, data]) => data);
  } else {
    data = filtered.value.reduce((prev, res) => {
      var index = valuesArr.findIndex(([k, v]) => k == res.e);
      if (prev[index]) {
        prev[index]++;
      } else {
        prev[index] = 1;
      }
      return prev;
    }, [])
  }
  return {
    labels,
    datasets: [
      {
        backgroundColor: ["#19CCBF", "#0970FF", "#F75B79", "#F78F2F", "#6CC43F", "#55A3EF", "#F3CC3A", "#E6EAEF"],
        data,
      },
    ],
  };
});
const statPie = ref(null);
const filtered = computed(() => {
  if (staticFiltered.value) {
    return result.value.Detail.filter((item) => {
      var stype = fitleredFileType.find((typ) => item.u.endsWith(typ));
      return !stype;
    });
  } else {
    return result.value.Detail;
  }
})
const shownDetail = computed(() => {

  return filtered.value.filter((item) => {
    var needFilter = false;
    if (staticFiltered.value) {
      needFilter |= fitleredFileType.includes((typ) => item.u.endsWith(typ))
    }

    if (detail_filter.value.error_type) {
      needFilter |= detail_filter.value.error_type != item.e
    }

    if (detail_filter.value.method) {
      needFilter |= detail_filter.value.method != item.m
    }

    if (detail_filter.value.status_code) {
      if (detail_filter.value.status_code.endsWith("XX")) {
        needFilter |= Math.floor(item.s / 100) != new Number(detail_filter.value.status_code[0])
      } else {
        needFilter |= detail_filter.value.status_code != item.s
      }
    }

    return !needFilter

  });
});

const static_filtered_cache = ref(null);
const detail_filter = ref({
  method: "",
  status_code: "",
  error_type: ""
});
const staticFiltered = computed({
  get() {
    if (static_filtered_cache.value == null) {
      var json = localStorage.getItem("static_filtered");
      static_filtered_cache.value = JSON.parse(json);
    }
    return static_filtered_cache.value;
  },
  set(to) {
    static_filtered_cache.value = to;
    var json = JSON.stringify(to);
    localStorage.setItem("static_filtered", json);
  },
});
const fitleredFileType = [".js", ".png", ".jpg", ".jpeg", ".js", ".css", ".html", "svg"];
const showTable = ref(false);
const currentTab = ref('brief');
const selectedClient = ref(null);
const shownDetailForClient = computed(() => {
  return shownDetail.value.filter(item =>
    item.c == selectedClient.value
  )
})
const showEcSelector = ref(false);
const filteredClients = computed(() => {
  return Object.entries(result.value.Clients).filter(arr => shownEc.value[arr[1].m])
})
const clients = ref(null);
const shownDialog = ref(false);
const selectedDetail = ref(null);
const extraAPI = ref(null);


watch(() => currentTab.value, () => {
  detail_filter.value = {
    method: "",
    status_code: "",
    error_type: ""
  };
})

onBeforeMount(() => {
  window.onpopstate = (event) => {
    var tab = event.path?.[0]?.location?.hash.replace("#", '')
    if (!tab) {
      tab = "brief"
    }
    currentTab.value = tab
    if (event.state && event.client) {
      selectedClient.value = event.client;
    }
  };
})

function handleClick(e) {
  const points = statPie.value.chart.getElementsAtEventForMode(
    e,
    "nearest",
    { intersect: true },
    true
  );


  if (points.length) {
    const firstPoint = points[0];
    const label = statPie.value.chart.data.labels[firstPoint.index];
    // const value =
    //   statPie.value.chart.data.datasets[firstPoint.datasetIndex].data[
    //   firstPoint.index
    //   ];
    if (detail_filter.value.error_type == label) {
      detail_filter.value.error_type = "";
    } else {
      detail_filter.value.error_type = Object.entries(Levels).find(([k, v]) => v == label)[0]
    }
  }
}

function getValueAtIndexOrDefault(value, index, defaultValue) {
  if (value === undefined || value === null) {
    return defaultValue;
  }

  // if (this.isArray(value)) {
  return index < value.length ? value[index] : defaultValue;
  // }

  // return value;
};

function fitLabel(chart) {

  chart.legend.afterFit = function () {
    var width = this.width;
    console.log(this);

    this.lineWidths = this.lineWidths.map(() => this.width - 12);

    this.options.labels.padding = 30;
    this.options.labels.boxWidth = 15;
  };

  var data = chart.data;
  //https://github.com/chartjs/Chart.js/blob/1ef9fbf7a65763c13fa4bdf42bf4c68da852b1db/src/controllers/controller.doughnut.js
  if (data.labels.length && data.datasets.length) {
    return data.labels.map((label, i) => {
      var meta = chart.getDatasetMeta(0);
      var ds = data.datasets[0];
      var arc = meta.data[i];
      var custom = arc && arc.custom || {};
      // var getValueAtIndexOrDefault = this.getValueAtIndexOrDefault;
      var arcOpts = chart.options.elements.arc;
      var fill = custom.backgroundColor ? custom.backgroundColor : getValueAtIndexOrDefault(ds.backgroundColor, i, arcOpts.backgroundColor);
      var stroke = custom.borderColor ? custom.borderColor : getValueAtIndexOrDefault(ds.borderColor, i, arcOpts.borderColor);
      var bw = custom.borderWidth ? custom.borderWidth : getValueAtIndexOrDefault(ds.borderWidth, i, arcOpts.borderWidth);

      return {
        text: label,
        fillStyle: fill,
        strokeStyle: stroke,
        lineWidth: 2,
        hidden: isNaN(ds.data[i]) || meta.data[i].hidden,

        // Extra data used for toggling the correct item
        index: i
      };
    });
  }
  return [];
}

function setStaticFilter() {
  staticFiltered.value = !staticFiltered.value;
}



function gotoClientPage(client) {
  history.pushState({
    client
  }, '', '#clients');
  currentTab.value = "clients";
  selectedClient.value = client;
}

function gotoPage(val) {
  history.pushState({}, '', '#' + val);
  currentTab.value = val;
}

function changeShowType() {
  showTable.value = !showTable.value;
}

function selectToHere(target) {
  target = new Number(target)
  for (let index = 9999; index <= target; index++) {
    shownEc.value[index] = true;
  }
  for (let index = target + 1; index <= 10007; index++) {
    shownEc.value[index] = false;
  }
}

function selectOnlyHere(target) {
  target = new Number(target)
  for (let index = 9999; index <= 10007; index++) {
    shownEc.value[index] = false;
  }
  shownEc.value[target] = true;
}

function hideEcSelector() {
  showEcSelector.value = false
}

function notifyClientsUI() {
  nextTick(() => {
    clients.value.update();
  })
}

function selectOneRecord(data) {
  console.log("here")
  selectedDetail.value = data;
  shownDialog.value = true;
}

function addToApi() {
  var record = selectedDetail.value;
  if (extraAPI.value == null) {
    extraAPI.value = {
      paths: {}
    }
  }
  var path = {}
  path[record.m] = {}
  extraAPI.value.paths[record.u] = path
}

function filterRecord(e) {
  detail_filter.value = e;
}
</script>

<template>
  <tabs v-model="currentTab" @input="gotoPage" style="height: auto;overflow: hidden;">
    <tab-item label="统计简报" name="brief">
      <panel label="访问统计" overflow-hidden>
        <template #toolbar>
          <button size="small" plain variant="primary" class="icon" @click="changeShowType">
            <chart-icon v-if="showTable" />
            <table-icon v-else></table-icon>
          </button>
        </template>
        <table v-if="showTable" class="brief-table">
          <tr v-for="label, index in briefData.labels" :key="inde">
            <th>{{ label }}</th>
            <td>{{ briefData?.datasets?.[0]?.data?.[index] }}</td>
          </tr>
        </table>
        <Doughnut :chart-data="briefData" :chart-options="{
          onClick: handleClick,
          elements: {
            center: [{
              text: filtered.length,
              color: '#0970FF',
              size: 1.1,
            }, {
              text: '数据总条数',
              color: '#666666',
              size: 0.8,
            }]
          },
          cutout: '70%',
          plugins: {
            legend: {
              position: 'bottom',
              align: 'start',
              labels: {
                padding: 20,
              }
            }
          }
        }" width="90%" height="90%" class="pie-chart" ref="statPie" v-else />
        <items :label-width="120" style="margin-top: 30px;">
          <item label="数据总条数：">
            {{ result.Detail.length }}条
          </item>
          <item label="过滤后总条数：">{{ filtered.length }}条</item>
          <item label="记录客户数">{{ result.ClientCount }}个</item>
        </items>
      </panel>
      <record-panel style="flex:1" :records="shownDetail" @select-client="gotoClientPage"
        @select-record="selectOneRecord" @set-filter="filterRecord" :filter="detail_filter"></record-panel>
    </tab-item>
    <tab-item label="客户报告" name="clients" @shown="notifyClientsUI">
      <panel label="客户端列表" style="max-width:300px" overflow-hidden>
        <template #toolbar>
          <button variant="primary" plain size="small" @click="showEcSelector = !showEcSelector"
            :selected="showEcSelector">
            <filter-icon></filter-icon>
          </button>
        </template>
        <pageul class="clients" ref="clients" style="height: 100%;">
          <pageli v-for="(v, k) in filteredClients" :key="k" :class="{ active: v[0] == selectedClient }"
            @click="selectedClient = v[0]">
            <div class="flex between inline">
              {{ v[0] }}<span class="badge" :class="'ec-badge-' + v[1].m">{{ v[1].m - 10000 }}</span>
            </div>
          </pageli>
        </pageul>
      </panel>
      <record-panel style="flex:1" :records="shownDetailForClient" :filter="detail_filter"></record-panel>
    </tab-item>
    <tab-item label="API定义" v-if="extraAPI" name="">
      <json-editor :doc="extraAPI"></json-editor>
    </tab-item>
  </tabs>
  <my-dialog v-model="shownDialog" header="添加到API定义" @confirm="addToApi">
    <h3>是否将以下URL添加到API定义中？</h3>
    <b>{{ selectedDetail.m }}&nbsp;</b>{{ selectedDetail.u }}
  </my-dialog>
  <div id="ec-selector" v-if="showEcSelector" v-on-click-outside="hideEcSelector">
    <div class="info">选择列表中需要展示的信息</div>
    <template v-for="k, v in labelDeses" :key="k">
      <div class="ec-selector-item">
        <input type="checkbox" :id="'ec-' + v" :name="'ec-' + v" v-model="shownEc[v]">
        <label @click="selectToHere(v)" @dblclick="selectOnlyHere(v)">[{{ v }}]{{ k }}
        </label>
      </div>
    </template>
  </div>
</template>

<style lang="scss" scoped>
.pie-chart {
  width: 70%;
  margin: 10px auto;
  margin-top: 1em;
}

.brief-table {

  margin: 20px;

  tr+tr {
    margin: 2px;
  }

  th {
    background-color: #fafafa;
    line-height: 1.4em;
    width: 150px;
    line-break: auto;
  }

  td {
    background-color: #f2f2f2;
    line-height: 1.4em;
    width: 100px;
    text-align: center;
    font-weight: bold;
  }
}

span.badge {
  width: 16px;
  height: 16px;
  background-color: #f4f4f4;
  display: block;
  text-align: center;
  line-height: 14px;
  font-weight: bold;
  color: white;
  border: 1px solid white;
  border-radius: 2px;

  &.ec-badge-10000 {
    background-color: #edfbf3;
    border-color: #97e5ba;
    color: #16c566;
  }

  &.ec-badge-10001 {
    background-color: #fffbe6;
    border-color: #ffe58f;
    color: #faad14;
  }

  &.ec-badge-10002 {
    background-color: #fff6f1;
    border-color: #fbcaaf;
    color: #f5894e;
  }

  &.ec-badge-10003 {
    background-color: #fef1ed;
    border-color: #f9ad99;
    color: #f2481c;
  }
}

#ec-selector {

  position: absolute;
  top: 155px;
  left: 262px;
  background: #FFFFFF;
  box-shadow: 0px 2px 12px 0px rgb(0 0 0 / 8%);
  border-radius: 4px;
  padding: 20px;

  .ec-selector-item {

    background-color: var(--apicat-success-light-color);
    ;
    line-height: 1.6em;
    padding: 6px 20px;
    display: flex;

    label {
      margin-left: 12px;
      flex: 1;
    }

    input {
      width: 16px;
      height: 16px;
      flex-shrink: 0;
      align-self: center;
      border-radius: 8px;
      // display: none;
      appearance: none;
      background-color: #fff;
      margin: 0;
      font: inherit;
      color: var(--apicat-primary-color);
      width: 1.15em;
      height: 1.15em;
      border: 0.15em solid var(--apicat-primary-color);
      border-radius: 2px;
      display: grid;
      place-content: center;
    }

    input::before {
      content: "";
      width: 0.65em;
      height: 0.65em;
      border-radius: 50%;
      transform: scale(0);
      transition: 80ms transform ease-in-out;
      clip-path: polygon(14% 44%, 0 65%, 50% 100%, 100% 16%, 80% 0%, 43% 62%);
      box-shadow: inset 1em 1em var(--apicat-primary-color);
    }

    input:checked {
      background-color: var(--apicat-primary-color);
      fill: white;

      &::before {
        box-shadow: inset 1em 1em white;
        transform: scale(1);
      }
    }
  }

  +.ec-selector-item {
    margin-top: 3px;
  }
}

.info {
  margin-top: -20px;
  margin-left: -20px;
  margin-right: -20px;
  background-color: #f0f0f0;
  padding: 10px 40px;
}
</style>